package org.malred.sqlSession;

import org.malred.Annotations.Delete;
import org.malred.Annotations.Insert;
import org.malred.Annotations.Select;
import org.malred.Annotations.Update;
import org.malred.config.XMLMapperBuilder;
import org.malred.pojo.Configuration;
import org.malred.pojo.MappedStatement;

import java.lang.reflect.*;
import java.util.List;
import java.util.Objects;

public class DefaultSqlSession implements SqlSession {
    private Configuration configuration;
    private Executor executor;

    public DefaultSqlSession(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public <E> List<E> selectList(String statementId, Object... params) {
        // 调用simpleExecutor里的query方法
        executor = new SimpleExecutor();
        // 根据statementId从configuration封装好的map里拿到statement
        MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
        List<Object> list = executor.query(configuration, mappedStatement, params);
        return (List<E>) list;
    }

    @Override
    public <T> T selectOne(String statementId, Object... params) {
        List<Object> objects = selectList(statementId, params);
        if (objects.size() == 1) {
            return (T) objects.get(0);
        } else {
            throw new RuntimeException("查询结果为空或查询结果过多");
        }
    }

    @Override
    public int update(String statementId, Object... params) {
        executor = new TransactionExecutor();
        // 根据statementId从configuration封装好的map里拿到statement
        MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
        int count = executor.update(configuration, mappedStatement, params);
        return count;
    }

    @Override
    public int delete(String statementId, Object... params) {
        executor = new TransactionExecutor();
        // 根据statementId从configuration封装好的map里拿到statement
        MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
        int count = executor.update(configuration, mappedStatement, params);
        return count;
    }

    @Override
    public int insert(String statementId, Object... params) {
        executor = new TransactionExecutor();
        // 根据statementId从configuration封装好的map里拿到statement
        MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
        int count = executor.update(configuration, mappedStatement, params);
        return count;
    }

    @Override
    public <T> T getMapper(Class<?> mapperClass) {
        // 使用JDK动态代理为Dao接口生成代理对象,并返回
        Object proxyInstance = Proxy.newProxyInstance(
                DefaultSqlSession.class.getClassLoader(),
                new Class[]{mapperClass},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 底层还是执行JDBC
                        // 准备参数 1: statementId: sql语句唯一标识 -> namespace.id
                        // 因为走mapper代理(invoke中)无法获取namespace和id,
                        // 所以xml里的namespace.id = 接口全限定类名.方法名

                        // 方法名
                        String methodName = method.getName();
                        // 方法所在类的名称
                        String className = method.getDeclaringClass().getName();

                        // xml里声明namespace+id就是statementid,在创建session时解析并存储
                        // 这里通过类名+方法名,然后去configuration里找
                        // xml里也要用类名+方法名来声明(如果是用代理)
                        String statementId = className + "." + methodName;

                        // 准备参数 2: params = args

                        // 获取被调用方法的返回值类型
                        Type genericReturnType = method.getGenericReturnType();

                        // 如果有注解
                        if (method.isAnnotationPresent(Select.class)) {
                            Select annotation = method.getAnnotation(Select.class);
                            ParseAnnotation(method, statementId, genericReturnType, annotation.value());
                        }
                        if (method.isAnnotationPresent(Update.class)) {
                            Update annotation = method.getAnnotation(Update.class);
                            ParseAnnotation(method, statementId, genericReturnType, annotation.value());
                        }
                        if (method.isAnnotationPresent(Delete.class)) {
                            Delete annotation = method.getAnnotation(Delete.class);
                            ParseAnnotation(method, statementId, genericReturnType, annotation.value());
                        }
                        if (method.isAnnotationPresent(Insert.class)) {
                            Insert annotation = method.getAnnotation(Insert.class);
                            ParseAnnotation(method, statementId, genericReturnType, annotation.value());
                        }

                        // 这里只是简单判断一下
                        // 判断是否进行了泛型类型参数化(是否有泛型)
                        if (genericReturnType instanceof ParameterizedType) {
//                            if (x instanceof Collection< ? >){
//                            }
//                            if (x instanceof Map<?,?>){
//                            }
                            List<Object> objects = selectList(statementId, args);
                            // 认为是集合
                            return objects;
                        }
                        // 返回值是基本类型 (int void)
                        if (genericReturnType.getTypeName().equals("int") ||
                                genericReturnType.getTypeName().equals("void")
                        ) {
                            // 修改 删除 插入 (int void)
                            return update(statementId, args);
                        }
                        return selectOne(statementId, args);
                    }
                });
        return (T) proxyInstance;
    }

    private void ParseAnnotation(Method method, String statementId, Type genericReturnType, String sqlTxt) {
        // sql信息封装
        MappedStatement mappedStatement = new MappedStatement();
        mappedStatement.setId(statementId);
        mappedStatement.setResultType(genericReturnType.getTypeName());
        Type[] genericParameterTypes = method.getGenericParameterTypes();
        // 暂时只支持一个接口方法参数(并且不能是基本类型)
        mappedStatement.setParamterType(genericParameterTypes[0].getTypeName());
        // 注解保存的sql
        mappedStatement.setSql(sqlTxt);
        // 存入configuration
        configuration.getMappedStatementMap().put(statementId, mappedStatement);
    }

    @Override
    public void commit() {
        // Can't call rollback when autocommit=true
        if (!configuration.isAutoCommit()) {
            executor.commit();
        }
    }

}
